#include "RayCasting.h"
#include "Ray.h"
#include "Scene.h"
#include "Object.h"
#include "Camera.h"
#include "Material.h"
#include "Intersection.h"
#include "Light.h"
#include <limits>
#include <QImage>
#include <math.h>
#include "utilities.h"

using namespace std;

namespace RayCasting
{

bool getIntersection(const Ray& ray, const list<Object*>& lista, Intersection *inter)
{
    inter->t = numeric_limits<float>::max();
    bool flag = false;

    for(list<Object*>::const_iterator i = lista.begin(); i != lista.end(); ++i)
    {
        Object *obj = *i;
        Intersection aux;

        if(obj->intersect(ray, &aux) && (aux.t < inter->t))
        {
                *inter = aux;
                flag = true;
        }

    }

    return flag;

}

QImage render(Scene* scene)
{
	list<Object*> lista = scene->getObjects();
    std::list<Light*> fontes = scene->getLights();
	Camera* cam = scene->getCamera();
    QImage img(cam->width(), cam->height(), QImage::Format_ARGB32);
    img.fill(qRgba(0, 0, 0, 255));

    for(int a=0; a < cam->height(); a++)
	{
        for(int b=0; b <cam->width(); b++)
		{
			Ray ray = scene->getCamera()->traceRay(a, b);
            Intersection inter;

            if(getIntersection(ray, lista, &inter))
			{
				Vector3f sum;
				Material *obj = inter.object->getMaterial();

				for(list<Light*>::iterator p=fontes.begin() ; p!=fontes.end() ; p++)
				{

					Point3f pointr = ray(inter.t); // ponto de intersecao com o objeto
					Vector3f v = ray.d*(-1);
					Vector3f li = ((*p)->getPosition()-pointr); // vetor para a luz atual
                    li.normalize();
                    float cosAng = dotProduct(li, inter.n);

                    if(cosAng > 0.f)
                    {
                        // acumulando parte difusa da luz
                        sum = sum + (obj->getDiffuse(inter.texCoord[0], inter.texCoord[1])*(*p)->getDiffuse())*dotProduct(li,inter.n);

                        // vetor de reflexao
                        Vector3f r = 2*(dotProduct(li, inter.n))*inter.n - li;
                        cosAng = dotProduct(r, v);

                        if(cosAng > 0.f)
                            // acumulando parte especular da luz
                            sum = sum + (obj->getSpecular()*(*p)->getSpecular()*pow(dotProduct(r, v),obj->getShininess()));
                    }
				}

				sum.x = (sum.x>1) ? 1:sum.x;
				sum.y = (sum.y>1) ? 1:sum.y;
				sum.z = (sum.z>1) ? 1:sum.z;
                img.setPixel(b, a, qRgba(sum.x*255, sum.y*255, sum.z*255, 255));

			}

		}

	}

	return img;
}


}
